/* eslint-disable @typescript-eslint/no-explicit-any */
import { arrow, computePosition, flip, offset, shift } from '@floating-ui/dom';
import {
  IModalData,
  IPopoverOptions,
  Modal,
  ViewMode,
} from '@ibiz-template/runtime';
import { useNamespace } from '@ibiz-template/vue3-util';
import {
  computed,
  defineComponent,
  onUnmounted,
  PropType,
  ref,
  VNode,
} from 'vue';
import './popover.scss';
import { OverlayPopoverContainer } from '../overlay-popover-container/overlay-popover-container';

/**
 * 计算飘窗显示
 *
 * @author chitanda
 * @date 2022-11-08 21:11:18
 * @param {HTMLElement} element
 * @param {HTMLElement} el
 * @param {HTMLElement} arrEl
 * @param {IPopoverOptions} opts
 * @return {*}  {Promise<void>}
 */
async function computePos(
  element: HTMLElement,
  el: HTMLElement,
  arrEl: HTMLElement,
  opts: IPopoverOptions,
): Promise<void> {
  const middlewareArr = [offset(opts.offsetOpts || 6), flip(), shift()];
  if (!opts.noArrow) {
    middlewareArr.push(arrow({ element: arrEl! }));
  }
  const options = await computePosition(element, el, {
    placement: opts.placement,
    strategy: 'absolute',
    middleware: middlewareArr,
  });
  {
    const { x, y, placement, middlewareData } = options;
    const { style } = el;
    style.left = `${x}px`;
    style.top = `${y}px`;

    if (!opts.noArrow) {
      // 箭头位置
      const { x: arrowX, y: arrowY } = middlewareData.arrow!;

      const staticSide: string = (
        {
          top: 'bottom',
          right: 'left',
          bottom: 'top',
          left: 'right',
        } as IData
      )[placement.split('-')[0]];

      Object.assign(arrEl.style, {
        left: arrowX != null ? `${arrowX}px` : '',
        top: arrowY != null ? `${arrowY}px` : '',
        right: '',
        bottom: '',
        [staticSide]: '-4px',
      });
    }
  }
}

const AppPopoverComponent = defineComponent({
  props: {
    opts: {
      type: Object as PropType<IPopoverOptions>,
      default: () => ({}),
    },
  },
  setup(props, ctx) {
    // 样式命名空间
    const ns = useNamespace('popover');
    // 是否显示
    const isShow = ref(false);
    // 是否悬浮在内容区，当悬浮在内容区时 autoClose 禁用
    const isHover = ref(false);
    // 跟 dom 元素
    const el = ref<HTMLDivElement>();
    // arrow dom 元素
    const arrEl = ref<HTMLDivElement>();
    // 是否可以自动关闭
    const autoClose = computed<boolean>(() => {
      return props.opts.autoClose === true && isHover.value === false;
    });

    // 鼠标入飘窗内容区
    function onMouseenter(e: MouseEvent): void {
      e.stopPropagation();
      isHover.value = true;
    }

    // 鼠标出飘窗内容区
    function onMouseleave(e: MouseEvent): void {
      e.stopPropagation();
      isHover.value = false;
    }

    const modal = new Modal({
      mode: ViewMode.POPOVER,
      viewUsage: 2,
      dismiss: (data: IModalData) => {
        ctx.emit('dismiss', data);
      },
    });

    let isCloseing = false;

    // 点击容器关闭飘窗
    async function dismiss(data?: IModalData) {
      isCloseing = true;
      await modal.dismiss(data);
      setTimeout(() => {
        isCloseing = false;
      }, 300);
    }

    // 组件销毁用于事件触发
    function destroy(e: Event): void {
      e.stopPropagation();
      if (autoClose.value && !isCloseing) {
        dismiss();
      }
    }

    function addEvents(): void {
      window.addEventListener('mousedown', destroy, { capture: true });
      window.addEventListener('blur', destroy, { capture: true });
      window.addEventListener('resize', destroy, { capture: true });
    }

    function removeEvents(): void {
      window.removeEventListener('mousedown', destroy, { capture: true });
      window.removeEventListener('blur', destroy, { capture: true });
      window.removeEventListener('resize', destroy, { capture: true });
    }

    addEvents();

    onUnmounted(() => {
      removeEvents();
    });

    /**
     * 飘窗显示并计算位置
     *
     * @author chitanda
     * @date 2022-11-09 12:11:04
     * @param {HTMLElement} target
     * @return {*}  {Promise<void>}
     */
    async function present(target: HTMLElement): Promise<void> {
      isShow.value = true;
      await computePos(target, el.value!, arrEl.value!, props.opts);
    }

    return {
      ns,
      el,
      arrEl,
      isShow,
      isHover,
      modal,
      present,
      dismiss,
      onMouseenter,
      onMouseleave,
    };
  },
  render() {
    return (
      <div
        class={[
          this.ns.b(),
          this.ns.is('show', this.isShow),
          this.ns.is('hover', this.isHover),
        ]}
        ref='el'
        onMouseenter={this.onMouseenter}
        onMouseleave={this.onMouseleave}
      >
        {!this.opts.noArrow && (
          <div class={[this.ns.e('arrow')]} ref='arrEl'></div>
        )}
        {this.$slots.default?.(this.modal)}
      </div>
    );
  },
});

/**
 * 创建飘窗
 *
 * @author chitanda
 * @date 2022-12-29 15:12:59
 * @export
 * @param {() => VNode} render
 * @param {IPopoverOptions} [opts]
 * @return {*}  {OverlayPopoverContainer}
 */
export function createPopover(
  render: () => VNode,
  opts?: IPopoverOptions,
): OverlayPopoverContainer {
  return new OverlayPopoverContainer(AppPopoverComponent, render, opts);
}
